package com.itheima.auth.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.auth.config.RsaKeyProperties;
import com.itheima.auth.domain.SysRole;
import com.itheima.auth.domain.SysUser;
import com.itheima.common.utils.JwtUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 14017
 * @date 2021.01.17
 * @description 重写security的登录过滤器
 */
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private RsaKeyProperties properties;

    public JwtLoginFilter(AuthenticationManager authenticationManager, RsaKeyProperties properties){
        this.authenticationManager = authenticationManager;
        this.properties = properties;
    }

    /**
     * @description (认证方法,首先会执行的方法) 重写该方法的作用,获取请求中的参数
     * @param request 请求对象
     * @param response 响应对象
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            // 将json对象信息转换成用户对象
            SysUser sysUser = new ObjectMapper().readValue(request.getInputStream(), SysUser.class);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword());
            return authenticationManager.authenticate(authenticationToken);
        } catch (Exception ex){
            // 没有通过认证, 返回异常信息
            try {
                response.setContentType("application/json;charset=utf-8");
                // 状态码设置为认证不通过 401
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                PrintWriter out = response.getWriter();
                Map<String, Object> map = new HashMap<>();
                map.put("code", HttpServletResponse.SC_UNAUTHORIZED);
                map.put("msg", "用户名或者密码错误");
                map.put("data", null);
                out.write(new ObjectMapper().writeValueAsString(map));
                out.flush();
                out.close();
            }catch (Exception e){
                logger.error("认证失败, 用户名或者密码错误" + e.getMessage());
                e.printStackTrace();
            }
            throw new RuntimeException(ex);
        }
    }

    /**
     * 上面的认证通过后才会执行该方法
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        SysUser sysUser = new SysUser();
        sysUser.setUsername(authResult.getName());
        sysUser.setRoles((List<SysRole>) authResult.getAuthorities());
        // 使用私钥进行加密来生成token
        String token = JwtUtils.generateTokenExpireInMinutes(sysUser, properties.getPrivateKey(), 24 * 60);
        response.addHeader("Authorization", "Bearer " + token);
        try {
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter out = response.getWriter();
            Map<String, Object> map = new HashMap<>();
            map.put("code", HttpServletResponse.SC_OK);
            map.put("msg", "认证通过");
            map.put("data", null);
            out.write(new ObjectMapper().writeValueAsString(map));
            out.flush();
            out.close();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}
